#include <rtthread.h>
#include <drivers/usb_host.h>

int rt_usb_hcd_alloc_pipe(uhcd_t hcd, upipe_t* pipe, uinst_t inst, uep_desc_t ep)
{
    struct upipe *p;
    int ret;

    p = (struct upipe *)rt_calloc(1, sizeof(struct upipe));
    if(!p)
    {
        return -1;
    }

    p->ep = *ep;
    p->inst = inst;

    if ((ret = hcd->ops->open_pipe(hcd, p)) == 0)
    {        
        rt_list_init(&p->urbhead);
        rt_list_init(&p->urbs);
        rt_list_init(&p->list);
        rt_list_init(&p->urbidle);
        rt_list_insert_before(&inst->pipe, &p->list);
        *pipe = p;
    }
    else
    {
        rt_free(p);
    }

    return ret;
}

int rt_usb_hcd_free_pipe(uhcd_t hcd, upipe_t pipe)
{
    RT_ASSERT(pipe != RT_NULL);
    hcd->ops->close_pipe(hcd, pipe);
    rt_list_remove(&pipe->list);
    rt_free(pipe);

    return 0;
}

int rt_usb_hcd_setup_xfer(uhcd_t hcd, upipe_t pipe, ureq_t setup, int timeout)
{
    return hcd->ops->pipe_xfer(hcd, pipe, USBH_PID_SETUP, (void *)setup, 8, timeout);
}

int rt_usb_hcd_port_reset(uhcd_t hcd, rt_uint8_t port)
{
    return hcd->ops->reset_port(hcd, port);
}

int rt_usb_hcd_pipe_xfer(uhcd_t hcd, upipe_t pipe, void* buffer, int nbytes, int timeout)
{
    rt_size_t remain_size;
    rt_size_t send_size;
    remain_size = nbytes;
    rt_uint8_t * pbuffer = (rt_uint8_t *)buffer;
    do
    {
        RT_DEBUG_LOG(RT_DEBUG_USB,("pipe transform remain size,: %d\n", remain_size));
        send_size = (remain_size > pipe->ep.wMaxPacketSize) ? pipe->ep.wMaxPacketSize : remain_size;
        if(hcd->ops->pipe_xfer(hcd, pipe, USBH_PID_DATA, pbuffer, send_size, timeout) == send_size)
        {
            remain_size -= send_size;
            pbuffer += send_size;
        }
        else
        {
            return 0;
        }
    }while(remain_size > 0);

    return nbytes;
}

int rt_usb_hcd_urb_submit(struct uhcd *hcd, struct upipe *pipe, struct urb *r)
{
    int ret;

    ret = hcd->ops->urb_enqueue(hcd, pipe, r);

    return ret;
}

int rt_usb_hcd_urb_cancel(struct uhcd *hcd, struct upipe *pipe, struct urb *r)
{
    hcd->ops->urb_dequeue(hcd, pipe, r);

    return 0;
}

int rt_usb_hcd_start(struct uhcd *hcd)
{
    return hcd->ops->start(hcd);
}

void rt_usb_hcd_event_urb_complete(struct urb *r, int status)
{
    rt_list_remove(&r->link);

    r->status = status;
    r->comp_cb(r);
}

int rt_usb_hcd_urb_link(struct upipe *pipe, struct urb *r, int isworking)
{
    if (!rt_list_isempty(&r->link))
        return -1;
    r->is_working = (rt_uint8_t)isworking;
    rt_list_insert_before(&pipe->urbhead, &r->link);

    return 0;
}

struct urb* rt_usb_hcd_urb_first(struct upipe *pipe)
{
    struct urb *r;

    if (!pipe)
        return RT_NULL;

    if (rt_list_isempty(&pipe->urbhead))
        return RT_NULL;

    r = rt_list_first_entry(&pipe->urbhead, struct urb, link);

    return r;
}
